//菱形继承(钻石继承)（单继承和多继承的复合组合）
#ifdef a
//继承顺序(对象模型)--》最底层是基类，从最上面由上至下按照基类继承顺序排下，基类内部的最下层是基类本身，最上层是最顶层的基类。
//即(_a,_b)(_a,_c)(_d)
#include<iostream>
using namespace std;
class A {
public:
	int _a;
};

class B : public A
{
public:
	int _b;
};

class C : public A
{
public:
	int _c;
};


class D : public B, public C
{
public:
	int _d;
};

int main() {
	D d;
	//菱形继承冗余性（B，C从最顶层基类A继承了两次_a,使得D继承B,C后有两个_a成员），因此共有成员_a,_a,_b,_c,_d五个
	cout << sizeof(D) << endl;//20字节

	//d._a = 0;//使用最底层子类访问最顶层基类成员时，出现访问不明确问题，因为有两个_a,分别来自对两个基类B，C的继承
		//解决方法1
			//当最底层基类访问最顶层成员的时候，直接加上其父类的名称（即通过作用域限定符明确访问成员）

		//解决方法2:
			//虚拟继承
	return 0;
}

#endif

	

	//虚拟继承（virtual）
#ifdef a
#include<iostream>
using namespace std;
class A {
public:
	int _a;
};

class B :  public A
{
public:
	int _b;
};
int main() {
	B b;
	cout << sizeof(b) << endl;
	//正常来说应该是8个字节，但虚拟继承中b为12个字节，多了4个字节，这4个字节是编译器自己对对象进行赋值的，而要对对象赋值初始化，需要
	//构造函数，因此这4个字节是编译器本身在构造对象时调用自动生成的构造函数给对象赋值的，若已经存在构造函数
	//则对已存在的进行适当修改
	b._a = 0;
	b._b = 1;
	//根据内存查看， 可发现这4个字节恰恰是对象的前
	//4个字节，并且还可以发现，虚拟继承中的对象模型子类在基类上面，即B在A上面:(4,_b)(_a)

	//从反汇编进行分析发现，当采用普通继承时
	//而当采用虚拟继承时，此处有汇编语句，说明在普通继承下，编译器甚至没有给该类生成默认的构造函数，虚拟继承下由于
	//需要进行前4个字节的赋值，所以有了汇编生成

	//可以发现对象的前4个字节可以作为地址指向一个称作虚基表/偏移量表的空间，作为地址的4个字节被称为
	//虚基表指针/偏移量表指针，即编译器通过对象的前4个字节找到了虚基表，而虚基表的前4个字节存放了子类对象
	//相对于本身的偏移量，即0x00 00 00 00,而偏移4个字节，即紧挨着的空间中，存放的是子类对象起始位置
	//相对于子类对象中基类部分的偏移量，然后通过该偏移量对子类对象的个性成员进行赋值。
	return 0;
}
#endif

//通常不会如此使用，占用空间较多，一般用于菱形虚拟继承，解决菱形继承的二义性/冗余性问题

//菱形虚拟继承的对象模型
#ifdef
#include<iostream>
using namespace std;
class A {
public:
	int _a;
};

class B : virtual public A
{
public:
	int _b;
};

class C : virtual public A
{
public:
	int _c;
};


class D :  public B, public C
{
public:
	int _d;
};

int main() {
	D d;
	//virtual应添加在B，C继承前
	//（B偏移量:20，_b）(C偏移量:16，_c) (_d) (_a),
	d._a = 1;
	d._b = 2;
	d._c = 3;
	d._d = 4;
	cout << sizeof(D) << endl;//24字节
	return 0;
}

#endif
